package ar.com.ford.it.fullEconomics.logic;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Vector;
import ar.com.ford.it.fullEconomics.FullEconomicsPrs;
import ar.com.ford.it.fullEconomics.exception.FullEconomicsException;
import ar.com.ford.it.fullEconomics.persistence.dao.CommodityPartDao;
import ar.com.ford.it.fullEconomics.persistence.dao.LogDao;
import ar.com.ford.it.fullEconomics.persistence.dao.MixDao;
import ar.com.ford.it.fullEconomics.persistence.dao.PartDao;
import ar.com.ford.it.fullEconomics.persistence.dto.Ncas012VehicleVw;
import ar.com.ford.it.fullEconomics.persistence.dto.Teesg01Plant;
import ar.com.ford.it.fullEconomics.persistence.dto.Teesp01Part;
import ar.com.ford.it.fullEconomics.persistence.dto.Teesp02Commodity;
import ar.com.ford.it.fullEconomics.persistence.dto.Teesp06Mix;
import ar.com.ford.it.fullEconomics.persistence.dto.Teess01Supplier;
import ar.com.ford.it.fullEconomics.persistence.dto.Teesx03Intflog;
import ar.com.ford.it.fullEconomics.persistence.dto.Teesx04Intflevent;
import ar.com.ford.it.fullEconomics.struct.CounterStruct;
import ar.com.ford.it.fullEconomics.struct.OptionCombo;
import ar.com.ford.it.fullEconomics.util.FullEconomicsDateUtil;
import ar.com.ford.it.fullEconomics.wslx.FullEconomicsWslxUser;
import ar.com.ford.it.webengine.persistence.PersistenceUtil;
import com.ford.it.logging.ILogger;
import com.ford.it.logging.Level;
import com.ford.it.logging.LogFactory;
import com.ford.it.persistence.PersistenceException;

public class PartLogic {
    private static final String CLASS_NAME = PartLogic.class.getName();

    private static final ILogger log = LogFactory.getInstance().getLogger(
            CLASS_NAME);

    /**
     * Obtiene los commodities por nombre
     * 
     * @param name
     * @param plant
     * @return
     */
    public Teesp01Part findPartByPk(Teesp01Part part)
            throws FullEconomicsException {

        String METHOD_NAME = "findPartByPk";
        log.entering(CLASS_NAME, METHOD_NAME);

        Teesp01Part result = new Teesp01Part();
        PartDao currDao = new PartDao();
        try {
            result = currDao.findPartByPk(part);
        } catch (PersistenceException e) {
            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Find Part By Name Exception", e);
            throw new FullEconomicsException(e,
                    FullEconomicsPrs.MessageErrors.Part.FINDBYNAME);
        }

        log.exiting(CLASS_NAME, METHOD_NAME);

        return result;
    }

    /**
     * devuelve las clases de partes
     * 
     * @return
     */
    public Collection<OptionCombo> getPartClases() {
        Vector<OptionCombo> options = new Vector<OptionCombo>();
        options.add(new OptionCombo("F", "Ford"));
        options.add(new OptionCombo("C", "Custom"));
        return options;
    }

    /**
     * Busca Parts segun code, name y en determinada planta
     * 
     * @param code
     * @return Parts que se correspondan con la busqueda.
     */
    public Collection<Teesp01Part> findParts(Teesp01Part findPart,
                                             Timestamp dateInMax,
                                             Timestamp dateOutMax)
            throws FullEconomicsException {

        String METHOD_NAME = "findParts";
        log.entering(CLASS_NAME, METHOD_NAME);

        PartDao dao = new PartDao();
        Collection<Teesp01Part> ret = null;

        try {
            ret = dao.findParts(findPart, dateInMax, dateOutMax);
        } catch (PersistenceException e) {
            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Find Parts Exception", e);
            throw new FullEconomicsException(e,
                    FullEconomicsPrs.MessageErrors.Part.FINDALL);
        }

        log.exiting(CLASS_NAME, METHOD_NAME);

        return ret;
    }

    /**
     * Busca Parts segun code, name y en determinada planta
     * 
     * @param code
     * @return Parts que se correspondan con la busqueda.
     */
    public Collection<Teesp01Part> findParts(Teesp01Part findPart,
                                             Timestamp dateInMax,
                                             Timestamp dateOutMax,
                                             String supplierClass,
                                             String supplierCode)
            throws FullEconomicsException {

        String METHOD_NAME = "findParts";
        log.entering(CLASS_NAME, METHOD_NAME);

        PartDao dao = new PartDao();
        Collection<Teesp01Part> ret = null;

        try {
            ret = dao.findParts(findPart, dateInMax, dateOutMax, supplierClass,
                    supplierCode);
        } catch (PersistenceException e) {
            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Find Parts Exception", e);
            throw new FullEconomicsException(e,
                    FullEconomicsPrs.MessageErrors.Part.FINDALL);
        }

        log.exiting(CLASS_NAME, METHOD_NAME);

        return ret;
    }

    /**
     * Busca Parts segun lowCommosity y en determinada planta
     * 
     * @param code
     * @return Parts que se correspondan con la busqueda.
     */
    public Collection<Teesp01Part> finLowCommodityParts(String lowCom,
                                                        Teesg01Plant plant)
            throws FullEconomicsException {

        String METHOD_NAME = "finLowCommodityParts";
        log.entering(CLASS_NAME, METHOD_NAME);

        PartDao dao = new PartDao();
        Collection<Teesp01Part> ret = null;

        try {
            ret = dao.finLowCommodityParts(lowCom, plant);
        } catch (PersistenceException e) {
            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Find Parts Exception", e);
            throw new FullEconomicsException(e,
                    FullEconomicsPrs.MessageErrors.Part.FINDALL);
        }

        log.exiting(CLASS_NAME, METHOD_NAME);

        return ret;
    }

    /**
     * Da de alta el Part pasado por parametro
     * 
     * @param part
     * Part a dar de alta
     * @param user
     * Usuario que da de alta el Part
     */
    public void createPart(Teesp01Part part, FullEconomicsWslxUser user)
            throws FullEconomicsException {

        String METHOD_NAME = "createPart";
        log.entering(CLASS_NAME, METHOD_NAME);

        PartDao dao = new PartDao();

        try {
            PersistenceUtil.newTransaction();
            part.setEesp01CreateUserC(user.getId());
            part.setEesp01LastUpdtUserC(user.getId());
            part.setEesp01CreateS(new Timestamp(System.currentTimeMillis()));
            part.setEesp01LastUpdtS(new Timestamp(System.currentTimeMillis()));
            dao.createPart(part);
            PersistenceUtil.commitTransaction();
        } catch (PersistenceException pe) {

            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "PersistenceException", pe);
            try {
                PersistenceUtil.rollbackTransaction();
            } catch (PersistenceException rollbackEx) {
                rollbackEx.printStackTrace();
            }

            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Create Part Exception", pe);
            throw new FullEconomicsException(pe,
                    FullEconomicsPrs.MessageErrors.Part.CREATE);
        }

        log.exiting(CLASS_NAME, METHOD_NAME);
    }

    /**
     * Actualiza el Part pasado por parametro
     * 
     * @param part
     * Part a actualizar
     * @param user
     * Usuario que actualiza el Part
     */
    public void updatePart(Teesp01Part part, FullEconomicsWslxUser user)
            throws FullEconomicsException {

        String METHOD_NAME = "updatePart";
        log.entering(CLASS_NAME, METHOD_NAME);

        PartDao dao = new PartDao();

        Teesp01Part workingCopy = null;

        try {
            PersistenceUtil.newTransaction();

            PersistenceUtil.getPersistenceManager().clearCache();

            workingCopy = dao.getPartForUpdate(part.getEesp01PartPrefixCodeC(),
                    part.getEesp01PartBaseCodeC(), part
                            .getEesp01PartSuffixCodeC(), part
                            .getEesp01PartClassCodeC(), user.getTeesg01Plant());
            workingCopy.setEesp01LastUpdtUserC(user.getId());
            workingCopy.setEesp01LastUpdtS(new Timestamp(System
                    .currentTimeMillis()));

            // Campos modificados, in y out date
            workingCopy.setEesp01PartDescriptionX(part
                    .getEesp01PartDescriptionX());
            workingCopy.setEesp01PartUnitmeasureCodeC(part
                    .getEesp01PartUnitmeasureCodeC());
            workingCopy.setEesp01PartInDateY(part.getEesp01PartInDateY());
            workingCopy.setEesp01PartOutDateY(part.getEesp01PartOutDateY());
            workingCopy.setEesp01PartCommodLowCodeC(part
                    .getEesp01PartCommodLowCodeC());
            workingCopy.setTeesx03Intflog(part.getTeesx03Intflog());

            PersistenceUtil.commitTransaction();

        } catch (PersistenceException pe) {

            try {
                PersistenceUtil.rollbackTransaction();
            } catch (PersistenceException rollbackEx) {
                rollbackEx.printStackTrace();
            }

            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Update Part Exception", pe);
            throw new FullEconomicsException(pe,
                    FullEconomicsPrs.MessageErrors.Part.UPDATE);
        }

        log.exiting(CLASS_NAME, METHOD_NAME);
    }

    /**
     * Elimina el part pasado por parametro
     * 
     * @param part
     * Part a eliminar
     */
    public void deletePart(Teesp01Part part) throws FullEconomicsException {

        String METHOD_NAME = "deletePart";
        log.entering(CLASS_NAME, METHOD_NAME);

        try {
            PersistenceUtil.newTransaction();

            PartDao dao = new PartDao();

            dao.deletePart(part);
            PersistenceUtil.commitTransaction();

        } catch (PersistenceException pe) {

            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Delete Part Exception", pe);
            throw new FullEconomicsException(pe,
                    FullEconomicsPrs.MessageErrors.Part.DELETE);

        }

        log.exiting(CLASS_NAME, METHOD_NAME);
    }

    /**
     * verifica si tiene objetos referenciados, devuelve falls si encuentra
     * alguno
     * 
     * @param part
     * Part a eliminar
     * @return boolean
     */
    public boolean isDeleteable(Teesp01Part part) throws FullEconomicsException {

        String METHOD_NAME = "isDeleatable";
        log.entering(CLASS_NAME, METHOD_NAME);

        boolean isDel = false;
        PartDao dao = new PartDao();
        try {
            isDel = dao.isDeleteable(part);
        } catch (PersistenceException pe) {
            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Delete Part Exception", pe);
            throw new FullEconomicsException(pe,
                    FullEconomicsPrs.MessageErrors.Part.DELETE);
        }
        log.exiting(CLASS_NAME, METHOD_NAME);
        return isDel;
    }

    /**
     * Elimina el part pasado por parametro y sus elementos relacionados
     * 
     * @param part
     * Part a eliminar
     */
    public void cascadeDelete(Teesp01Part part) throws FullEconomicsException {

        String METHOD_NAME = "cascadeDelete";
        log.entering(CLASS_NAME, METHOD_NAME);

        try {
            PersistenceUtil.newTransaction();

            PartDao dao = new PartDao();

            dao.cascadeDelete(part);
            PersistenceUtil.commitTransaction();

        } catch (PersistenceException pe) {

            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Delete Part Exception", pe);
            throw new FullEconomicsException(pe,
                    FullEconomicsPrs.MessageErrors.Part.DELETE);

        }

        log.exiting(CLASS_NAME, METHOD_NAME);
    }

    /**
     * Busca las partes sin estructura de costos y con el filtro especificado.
     * Primero busca todas las partes, busca por otro lado las partes con
     * estructura de costo y las remueve.
     * 
     * 
     * @param code
     * @return Parts que se correspondan con la busqueda.
     */
    public Collection<Teesp01Part> findPartsWithoutCostStr(
                                                           Teesp01Part findPart,
                                                           Timestamp dateInMax,
                                                           Timestamp dateOutMax,
                                                           String supplierClass,
                                                           String supplierCode,
                                                           String commod,
                                                           String vehicleLine)
            throws FullEconomicsException {

        String METHOD_NAME = "findParts";
        log.entering(CLASS_NAME, METHOD_NAME);

        Collection<Teesp01Part> ret = null;
        Collection<Teesp01Part> partsWithCostStr = null;
        MixDao mixDao = new MixDao();
        CommodityPartDao commPartDao = new CommodityPartDao();
        PartDao partDao = new PartDao();
        try {
            Teess01Supplier supplier = new Teess01Supplier();
            Teesp02Commodity commodity = new Teesp02Commodity();
            supplier.setEess01SupplierClassCodeC(supplierClass);
            supplier.setEess01SupplierCodeC(supplierCode);
            supplier.setEess01PlantCodeC(findPart.getEesp01PlantCodeC());
            commodity.setEesp02CommodityNumberK(new Integer(commod));
            commodity.setEesp02PlantCodeC(findPart.getEesp01PlantCodeC());
            partsWithCostStr = partDao.findPartsWithCostStructure(supplier,
                    commodity, findPart);
            ret = partDao.findParts(findPart, dateInMax, dateOutMax,
                    supplierClass, supplierCode);
            ret.removeAll(partsWithCostStr);
            Collection<Teesp01Part> partsToRemove = new LinkedList<Teesp01Part>();
            if (!commod.equalsIgnoreCase("-1")) {
                Collection<Teesp01Part> partsInCommod = commPartDao
                        .findCommodityParts(commodity);
                for (Teesp01Part part : ret) {
                    if (!partsInCommod.contains(part))
                        partsToRemove.add(part);
                }
                ret.removeAll(partsToRemove);
            }
            if (!vehicleLine.equalsIgnoreCase("-1")) {
                Teesp06Mix mix = new Teesp06Mix();
                Teesp01Part noPart = new Teesp01Part();
                Ncas012VehicleVw vhLine = new Ncas012VehicleVw();
                vhLine.setCas012VehicleCodeC(vehicleLine);
                mix.setEesp06PlantCodeC(findPart.getEesp01PlantCodeC());
                mix.setTeesp01Part(noPart);
                mix.setEesp06VehicleCodeC(vhLine);
                Collection<Teesp06Mix> mixColl = mixDao.findMix(mix);
                partsToRemove.clear();
                for (Teesp01Part part : ret) {
                    for (Teesp06Mix mixTemp : mixColl) {
                        if (mixTemp.getTeesp01Part().getEesp01PartClassCodeC()
                                .equalsIgnoreCase(
                                        part.getEesp01PartClassCodeC())
                                && mixTemp
                                        .getTeesp01Part()
                                        .getEesp01PartPrefixCodeC()
                                        .equalsIgnoreCase(
                                                part.getEesp01PartPrefixCodeC())
                                && mixTemp.getTeesp01Part()
                                        .getEesp01PartBaseCodeC()
                                        .equalsIgnoreCase(
                                                part.getEesp01PartBaseCodeC())
                                && mixTemp
                                        .getTeesp01Part()
                                        .getEesp01PartSuffixCodeC()
                                        .equalsIgnoreCase(
                                                part.getEesp01PartSuffixCodeC())) {
                            partsToRemove.add(mixTemp.getTeesp01Part());
                        }
                    }
                }
                ret.removeAll(partsToRemove);
            }
        } catch (PersistenceException e) {
            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME,
                    "Find Parts without cost structure Exception", e);
            throw new FullEconomicsException(e,
                    FullEconomicsPrs.MessageErrors.Part.FINDALL);
        }

        log.exiting(CLASS_NAME, METHOD_NAME);

        return ret;
    }

    public CounterStruct createOrUpdateWIPS(Collection<Teesp01Part> parts,
                                     String userId, Teesx03Intflog logNumber)
            throws FullEconomicsException {
        String METHOD_NAME = "createOrUpdateWIPS";
        log.entering(CLASS_NAME, METHOD_NAME);
        Teesp01Part workingCopy = new Teesp01Part();
        PartDao partDao = new PartDao();
        Collection<Teesg01Plant> plantasObj;
        LogDao logDao = new LogDao();
        LogProcessLogic logLogic = new LogProcessLogic();
        boolean findPlant = false;
        int count = 0;
        CounterStruct counter = new CounterStruct();
        try {
            plantasObj = partDao.findAll(new Teesg01Plant(), null, null);
            for (Teesp01Part aPart : parts) {
                for (Teesg01Plant planta : plantasObj) {
                    if (planta.getEesg01PlantAlternateCodeC().equals(
                            aPart.getEesp01PlantCodeC()
                                    .getEesg01PlantAlternateCodeC())) {
                        count++;
                        PersistenceUtil.newTransaction();
                        logNumber = logDao.getIntflogyUpdate(logNumber);
                        aPart.setEesp01PlantCodeC(planta);
                        workingCopy = partDao.getPartForUpdate(aPart
                                .getEesp01PartPrefixCodeC(), aPart
                                .getEesp01PartBaseCodeC(), aPart
                                .getEesp01PartSuffixCodeC(), "F", planta);
                        // Compruebo que no se halla modificado ninguna pk en
                        // caso de que se alla realizado alguna modificacion en
                        // la pk se vuelve a crear el registro
                        if (workingCopy != null) {
                            // Actualizo el Teesp01Part
                            String desc = this.modificationDescriptio(
                                    workingCopy, aPart);
                            workingCopy.setEesp01PartDescriptionX(aPart
                                    .getEesp01PartDescriptionX());
                            workingCopy.setEesp01PartUnitmeasureCodeC(aPart
                                    .getEesp01PartUnitmeasureCodeC());
                            workingCopy.setEesp01PartCommodLowCodeC(aPart
                                    .getEesp01PartCommodLowCodeC());
                            workingCopy.setEesp01LastUpdtUserC(userId);
                            workingCopy.setEesp01LastUpdtS(new Timestamp(System
                                    .currentTimeMillis()));
                            workingCopy.setTeesx03Intflog(logNumber);
                            logLogic.insertLogIntfLevent(userId, logNumber,
                                    "U", desc, count);
                            counter.addCount();
                        } else {
                            aPart.setTeesx03Intflog(logNumber);
                            FullEconomicsWslxUser user = new FullEconomicsWslxUser(
                                    userId);
                            this.createPart(aPart, user);
                            counter.addCount();
                        }
                        findPlant = true;
                        PersistenceUtil.commitTransaction();
                    } // Cierre de la existencia de la planta
                }// Cierre del ciclo for por planta
                if (!findPlant) {
                    logLogic.insertLogIntfLevent(userId, logNumber, "W",
                            "Plant '"
                                    + aPart.getEesp01PlantCodeC()
                                            .getEesg01PlantAlternateCodeC()
                                    + "' not found", count);
                    counter.addWarning();
                }
                findPlant = false;
            }// Cierre del ciclo de piezas

        } catch (PersistenceException e) {
            logLogic.insertLogIntfLevent(userId, logNumber, "E",
                    "Internal error. proccess canceled", count);
            counter.addError();
            try {
                this.wipsRollBack(logNumber, userId);
            } catch (PersistenceException e1) {
                logLogic.insertLogIntfLevent(userId, logNumber, "E",
                        "RollBack error", count);
                counter.addError();
            }
            log.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME, "update Supplier",
                    e);
            throw new FullEconomicsException(e,
                    FullEconomicsPrs.MessageErrors.Part.CREATE);
        }
        log.exiting(CLASS_NAME, METHOD_NAME);
        return counter;
    }

    private void wipsRollBack(Teesx03Intflog logNumber, String userId)
            throws PersistenceException, FullEconomicsException {
        PartDao dao = new PartDao();
        String[] fields = { "teesx03Intflog" };
        Object[] values = { logNumber };
        String[] eventFields = { "teesx03Intflog", "eesx04IevntTypeCodeC" };
        Object[] eventValues = { logNumber, "U" };

        // busco las modificaciones
        Collection<Teesx04Intflevent> modifEvents = dao.findAll(
                new Teesx04Intflevent(), eventFields, eventValues);
        for (Teesx04Intflevent event : modifEvents) {
            Teesp01Part originalPart = this.getOriginalPart(event);
            FullEconomicsWslxUser user = new FullEconomicsWslxUser(userId);
            user.setTeesg01Plant(originalPart.getEesp01PlantCodeC());
            this.updatePart(originalPart, user);

        }

        // borro las piezas insertadas
        Collection<Teesp01Part> parts = dao.findAll(new Teesp01Part(), fields,
                values);
        for (Teesp01Part part : parts)
            this.deletePart(part);

    }

    private String modificationDescriptio(Teesp01Part originalPart,
                                          Teesp01Part newPart) {
        String desc = "Part modified: Plant="
                + originalPart.getEesp01PlantCodeC().getEesg01PlantCodeC()
                + "; Part=" + originalPart.toString() + ". Changed";
        desc += " Description= " + originalPart.getEesp01PartDescriptionX()
                + " to " + newPart.getEesp01PartDescriptionX();
        desc += "; Unit measure="
                + originalPart.getEesp01PartUnitmeasureCodeC() + " to "
                + newPart.getEesp01PartUnitmeasureCodeC();
        desc += "; Wips commodity="
                + originalPart.getEesp01PartCommodLowCodeC() + " to "
                + newPart.getEesp01PartCommodLowCodeC();
        desc += ". Additional Info: Part in date="
                + FullEconomicsDateUtil.format(originalPart
                        .getEesp01PartInDateY())
                + "; Part out date="
                + FullEconomicsDateUtil.format(originalPart
                        .getEesp01PartOutDateY()) + ".";

        return desc;
    }

    private Teesp01Part getOriginalPart(Teesx04Intflevent event) {
        String desc = event.getEesx04IevntMessageDescripX();
        Teesp01Part part = new Teesp01Part();
        Teesg01Plant plant = new Teesg01Plant();
        int plantIndex = desc.indexOf("Plant");
        int partIndex = desc.indexOf("Part", plantIndex);
        int changeIndex = desc.indexOf("Changed");
        int descIndex = desc.indexOf("Description");
        int descEndIndex = desc.indexOf(" to ", descIndex);
        int unitIndex = desc.indexOf("Unit measure");
        int unitEndIndex = desc.indexOf(" to ", unitIndex);
        int commodityIndex = desc.indexOf("commodity");
        int commodityEndIndex = desc.indexOf(" to ", commodityIndex);
        int inDateIndex = desc.indexOf("Part in date");
        int outDateIndex = desc.indexOf("Part out date");
        String plantCode = desc.substring(plantIndex + 6, partIndex - 2);
        String codes[] = desc.substring(partIndex + 7, changeIndex - 2).split(
                "-");
        Calendar dateIn = FullEconomicsDateUtil.format(desc.substring(
                inDateIndex + 13, inDateIndex + 23));
        Calendar dateOut = FullEconomicsDateUtil.format(desc.substring(
                outDateIndex + 14, outDateIndex + 24));
        plant.setEesg01PlantCodeC(plantCode);
        part.setEesp01PlantCodeC(plant);
        part.setEesp01PartClassCodeC("F");
        part.setEesp01PartPrefixCodeC(codes[0]);
        part.setEesp01PartBaseCodeC(codes[1]);
        part.setEesp01PartSuffixCodeC(codes[2]);
        part.setEesp01PartDescriptionX(desc.substring(descIndex + 13,
                descEndIndex));
        part.setEesp01PartUnitmeasureCodeC(desc.substring(unitIndex + 13,
                unitEndIndex));
        part.setEesp01PartCommodLowCodeC(desc.substring(commodityIndex + 10,
                commodityEndIndex));
        part.setEesp01PartInDateY(new Timestamp(dateIn.getTimeInMillis()));
        part.setEesp01PartOutDateY(new Timestamp(dateOut.getTimeInMillis()));
        return part;
    }

}